/*---------------------------------------------------------------------
 * File name: modbus.h
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#pragma once
/*
 */
#include <stdint.h>
/*
*/
#define MODBUS_CMD_RIBS  0x02   //read input bits
#define MODBUS_CMD_RKBS  0x01   //read keep bits
#define MODBUS_CMD_WKB   0x05   //write single keep bit
#define MODBUS_CMD_WKBS  0x0F   //write keep bits
#define MODBUS_CMD_RIRS  0x04   //read input registers
#define MODBUS_CMD_RKRS  0x03   //read keep registers
#define MODBUS_CMD_WKR   0x06   //write single keep register
#define MODBUS_CMD_WKRS  0x10   //write keep registers
#define MODBUS_CMD_WRT   0x55   //write read test
#define MODBUS_ACK_ERR   0xFF
/*
*/
typedef enum mdet_s
{
  MDET_OK,
  MDET_WAITACK,
  MDET_BADID,
  MDET_BADACK,
  MDET_BADCRC,
  MDET_BADDATA,
  MDET_BADFRAME,
  MDET_TIMEOUT
}mdet_t;
/*
*/
class ModbusBase
{
public:
  ModbusBase(uint8_t my_id0);
  
  struct {
    uint8_t id;
    uint8_t cmd;
    uint16_t address;
    uint16_t number;
    uint8_t bytes;
    uint8_t *data;
    uint16_t crc;
  }pdu;

  uint8_t buffer0[300];
  int data_length;
  uint8_t my_id;
  uint8_t packet_mask;  //bit0:address  bit1:number  bit2:byte
  
  
  /* overrid */
  virtual int packet(uint8_t **ptr);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address, uint16_t number, uint8_t bytes);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address, uint16_t number);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address);
  virtual void build_start(uint8_t id, uint8_t cmd, uint8_t bytes);
  virtual void build_start(uint8_t id, uint8_t cmd);
  virtual void build_end();
  /* end overrid */
  uint16_t byte_crc(uint16_t crc, uint8_t data);
  uint16_t calc_crc(uint16_t crc, uint8_t *buffer, uint16_t len);
  uint16_t calc_crc(uint8_t *buffer, uint16_t len);
  uint16_t calc_crc_ex(uint8_t *buffer, uint16_t len);
  void data_copy(uint8_t *buffer, int offset, int num);
  
  void insert_u8(uint8_t data);
  void insert_u16(uint16_t data);
  void insert_u24(uint32_t data);
  void insert_u32(uint32_t data);
  void insert_f32(float data);
  void insert_bytes(uint8_t *buffer, int number);
  void insert_string(char *str);
  
  uint8_t  extract_u8(int index);
  uint16_t extract_u16(int index);
  uint32_t extract_u24(int index);
  uint32_t extract_u32(int index);
  float extract_f32(int index);
  uint32_t extract_bytes(uint8_t *buffer, int number, int index);
  uint32_t extract_string(char *str, int index);
  
  int packet_read(uint8_t **ptr, uint8_t slave_id, uint8_t cmd, uint16_t address, uint16_t number);
  int packet_write_single(uint8_t **ptr, uint8_t slave_id, uint8_t cmd, uint16_t address, uint16_t data);
  int packet_write_multiply(uint8_t **ptr, uint8_t slave_id, uint8_t cmd, uint16_t address, uint16_t number, uint16_t *data);
  int packet_write_read_test(uint8_t **ptr, uint8_t slave_id, uint8_t number, uint8_t *data);
  
  int packet_read_input_bits(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number) {
    return packet_read(ptr, slave_id, MODBUS_CMD_RIBS, address, number);
  }
  int packet_read_keep_bits(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number) {
    return packet_read(ptr, slave_id, MODBUS_CMD_RKBS, address, number);
  }
  int packet_write_keep_bit(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t data) {
    return packet_write_single(ptr, slave_id, MODBUS_CMD_WKB, address, data);
  }
  int packet_write_keep_bits(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number, uint16_t *data) {
    return packet_write_multiply(ptr, slave_id, MODBUS_CMD_WKBS, address, number, data);
  }
  int packet_read_input_registers(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number) {
    return packet_read(ptr, slave_id, MODBUS_CMD_RIRS, address, number);
  }
  int packet_read_keep_registers(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number) {
    return packet_read(ptr, slave_id, MODBUS_CMD_RKRS, address, number);
  }
  int packet_write_keep_register(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t data) {
    return packet_write_single(ptr, slave_id, MODBUS_CMD_WKR, address, data);
  }
  int packet_write_keep_registers(uint8_t **ptr, uint8_t slave_id, uint16_t address, uint16_t number, uint16_t *data) {
    return packet_write_multiply(ptr, slave_id, MODBUS_CMD_WKRS, address, number, data);
  }
  int packet_read_ack(uint8_t **ptr, uint16_t *data);
  int packet_write_ack(uint8_t **ptr);
  int packet_error_ack(uint8_t **ptr);
  
  mdet_t master_parse(uint8_t *buffer, int len, bool crc_enable);
  mdet_t slave_parse(uint8_t *buffer, int len, bool crc_enable);
};


/****************************************
 *          ModbusRTU
 *****************************************/
class ModbusRTU : public ModbusBase
{
public:
  ModbusRTU(uint8_t my_id) :
    ModbusBase(my_id)
  {}
  
  mdet_t master_parse(uint8_t *buffer, int len) {
    return ModbusBase::master_parse(buffer, len, true);
  }
  mdet_t slave_parse(uint8_t *buffer, int len) {
    return ModbusBase::slave_parse(buffer, len, true);
  }
};

/****************************************
 *          ModbusTCP
 *****************************************/
class ModbusTCP : public ModbusBase
{
public:
  ModbusTCP(uint8_t my_id, uint16_t head) :
    ModbusBase(my_id)
  {
    mbap.head = head;
    mbap.type = 0;
    mbap.length = 0;
  }
  
  //MBAP
  struct
  {
    uint16_t head;
    uint16_t type;
    uint16_t length;
  }mbap;
  
  virtual int packet(uint8_t **ptr);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address, uint16_t number, uint8_t bytes);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address, uint16_t number);
  virtual void build_start(uint8_t id, uint8_t cmd, uint16_t address);
  virtual void build_start(uint8_t id, uint8_t cmd, uint8_t bytes);
  virtual void build_start(uint8_t id, uint8_t cmd);
  virtual void build_end();
  
  mdet_t master_parse(uint8_t *buffer, int len);
  mdet_t slave_parse(uint8_t *buffer, int len);
};





